package com.freeter.modules.gjs.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.freeter.modules.gjs.entity.*;
import com.freeter.modules.gjs.mapper.*;
import com.freeter.modules.gjs.service.EmailService;
import com.freeter.modules.gjs.service.UseRecordService;
import org.apache.logging.log4j.LogManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.logging.log4j.Logger;
import java.math.BigDecimal;
import java.util.*;
import com.freeter.modules.gjs.dto.DivisionsSitesDetailDTO;

/**
 * 业务逻辑处理器
 * 负责处理各种业务文件的逻辑，从Node.js版本移植
 */
@Service
public class BusinessLogicProcessor {
    
    private static final Logger log = LogManager.getLogger(BusinessLogicProcessor.class);
    
    @Autowired
    private GrMapper grMapper;
    @Autowired
    private GrItemMapper grItemMapper;
    @Autowired
    private StockStatusMapper stockStatusMapper;
    @Autowired
    private TransactionMapper transactionMapper;
    @Autowired
    private MstrSiteMapper mstrSiteMapper;
    @Autowired
    private MstrItemsMapper mstrItemsMapper;
    @Autowired
    private MstrUomMapper mstrUomMapper;
    @Autowired
    private MstrCat00Mapper mstrCat00Mapper;
    @Autowired
    private MstrCat01Mapper mstrCat01Mapper;
    @Autowired
    private MstrCat02Mapper mstrCat02Mapper;
    @Autowired
    private TransferRequestMapper transferRequestMapper;
    @Autowired
    private DamageReportMapper damageReportMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private TransferItemsMapper transferItemsMapper;
    @Autowired
    private UseRecordService useRecordService;
    @Autowired
    private EmailService emailService;
    @Autowired
    private DivisionsSitesMapper divisionsSitesMapper;
    @Autowired
    private UsersSitesMapper usersSitesMapper;
    @Autowired
    private DamageReportTokenLinkMapper damageReportTokenLinkMapper;


    /**
     * 处理收货单文件
     */
    @Transactional(rollbackFor = Exception.class)
    public void processGrTransaction(String grId) {
        log.info("开始处理收货单 id: {}", grId);
        try {
            Gr gr = this.grMapper.selectById(grId);
            // 1. 处理项目（站点）主数据
            Long siteId = processSiteData(gr.getProjectno());
            gr.setRecvSiteId(siteId);
            
            String action = gr.getAction();
            //如果是cancel update 先冲销
            if ("CANCEL".equals(action) ||"UPDATE".equals(action) ) {
                //冲销库存
                chongxiaoStock(gr);
            }
            //如果是cancel 冲销完后不做后续库存和库存记录的处理
            if ("CANCEL".equals(action)) {
               return ;
            }
            processGrAdd(gr);
            // 3. 更新收货单状态
            updateGrStatus(gr.getId());
        } catch (Exception e) {
            log.error("处理收货单失败 id: {}",grId,  e);
            Gr tmp = new Gr();
            tmp.setId(Long.valueOf(grId));
            tmp.setReCalcStatus(2);
            tmp.setCalcStatus(0);
            this.grMapper.updateById(tmp);
        }
    }

    private Gr getPrevGr(Gr gr) {
        if (gr == null || gr.getRefno() == null || gr.getProjectno() == null || gr.getRecordTime() == null) {
            return null;
        }
        Wrapper<Gr> wrapper = new EntityWrapper<>();
        wrapper.eq("refno", gr.getRefno())
                .eq("projectno", gr.getProjectno())
                .lt("record_time", gr.getRecordTime())
                .orderBy("record_time", false)
                .last("limit 1");
        List<Gr> list = grMapper.selectList(wrapper);
        return (list != null && !list.isEmpty()) ? list.get(0) : null;
    }

    /**
     * 根据site id, uomId 和 item barcode 查找库存记录
     * @param siteId
     * @param uomId
     * @param itemBarcode
     * @return
     */
    private StockStatus getStockStatus(Long siteId, Long uomId,String itemBarcode) {
        StockStatus stockQuery = new StockStatus();
        stockQuery.setSiteId(siteId);
        stockQuery.setItemUomId(uomId);
        stockQuery.setItemBarcode(itemBarcode);
        return  stockStatusMapper.selectOne(stockQuery);
    }

    /**
     * 写冲销交易记录
     * @param oldItem
     * @param tmp
     * @param oldGr
     * @param gr
     */
    private void insertChonxiaoTransation(GrItem oldItem,  StockStatus tmp, Gr oldGr,Gr gr) {
        Transaction trx = new Transaction();
        trx.setStockId(tmp.getId());
        trx.setSuppName(oldGr.getSuppnam());
        trx.setQty(oldItem.getQty().negate()); // 负数
        trx.setRemarks("GR冲销");
        trx.setUponCalc( new BigDecimal(tmp.getStockQty()));
        trx.setUpdateRemarks("新的GR gr_id"+gr.getId()+"冲销旧的gr gr_id"+ oldGr.getId()+" action:"+gr.getAction());
        trx.setReCalcStatus(1);
        trx.setIsDeleted(0);
        trx.setCalcStatus(1);
        trx.setSource("GR");
        trx.setRecordTime(new Date());
        trx.setRefNo(oldGr.getRefno());
        trx.setPoNo(oldGr.getPono());
        trx.setPrNo(oldGr.getPrno());
        trx.setScForm(oldItem.getScform());
        trx.setDnDirName(oldGr.getDnDirName());
        trx.setTmmUrl(oldGr.getUrl());
        trx.setRecvSiteId(oldGr.getRecvSiteId());
        trx.setGrId(oldGr.getId());
        trx.setGrItemId(oldItem.getId());
        trx.setIsTransfer(0);
        trx.setRecordTime(gr.getRecordTime());
        this.transactionMapper.insert(trx);
    }

    /**
     * 冲销库存和记录冲销库存变动记录
     * @param gr
     */
    private void chongxiaoStock(Gr gr) {
        if (!gr.getAction().equals("UPDATE") && !gr.getAction().equals("CANCEL")) {
            return;
        }
        Gr oldGr  = getPrevGr(gr);
        if(oldGr == null){
            return;
        }

        Wrapper<GrItem> queryOldItem = new EntityWrapper<>();
        queryOldItem.eq("gr_id", oldGr.getId());
        List<GrItem> oldGrItems = grItemMapper.selectList(queryOldItem);
        if(oldGrItems == null && oldGrItems.isEmpty()){
            return;
        }
        //projectNo 转site_id
        Long siteId = processSiteData(oldGr.getProjectno());
        oldGr.setRecvSiteId(siteId);
        Date currentDate = new Date();
        for (GrItem oldItem : oldGrItems) {
            //补全明细相关主数据
            // 处理计量单位
            Long uomId = processUomData(oldItem.getUom());
            //uomid 保存到gr item
            oldItem.setUomId(uomId);
            // 处理物品主数据
            processItemData(oldItem, uomId);

            StockStatus oldStockStatus = getStockStatus(oldGr.getRecvSiteId(),oldItem.getUomId(),oldItem.getItemBarcode() );
            if(oldStockStatus ==null){
                log.error("冲销时没有找到对应的库存 gr_id:{}", gr.getId());
                throw new RuntimeException("冲销时没有找到对应的库存 gr_id:"+ gr.getId());
            }
            //冲减
            if (oldStockStatus != null) {
                StockStatus tmp = new StockStatus();
                tmp.setId(oldStockStatus.getId());
                BigDecimal stockQty = new BigDecimal(oldStockStatus.getStockQty());
                stockQty = stockQty.subtract(oldItem.getQty());
                tmp.setStockQty(stockQty.doubleValue() );
                tmp.setLastUpdate(gr.getRecordTime());

                stockStatusMapper.updateById(tmp);
                //记录冲销记录
                insertChonxiaoTransation(oldItem,  tmp, oldGr, gr);
            }
        }
    }
    
    /**
     * 处理收货单新增操作
     */
    @Transactional(rollbackFor = Exception.class)
    private void processGrAdd(Gr gr) {
        log.info("处理收货单库存和交易记录操作: gr_id {}", gr.getId());
            // 1. 处理收货单明细
            List<GrItem> grItems = selectGrItemsByGrId(gr.getId());
            for (GrItem item : grItems) {
                // 处理计量单位
                Long uomId = processUomData(item.getUom());
                //uomid 保存到gr item
                item.setUomId(uomId);
                // 处理主数据 mstr_item
                processItemData(item, uomId);
                // 处理库存状态
                StockStatus stock = processStockStatus(item, gr.getRecvSiteId(), item.getItemBarcode(), uomId,gr.getRecordTime());
                // 处理交易记录
                Long transactionId = processTransactionRecord(gr, item, stock);
            }
            log.info("收货单新增处理完成: gr_id {}", gr.getId());
    }

    private List<GrItem> selectGrItemsByGrId(Long grId) {
        Wrapper<GrItem> wrapper = new EntityWrapper<>();
        wrapper.eq("gr_id",grId);
        return this.grItemMapper.selectList(wrapper);
    }

    
    /**
     * 处理Use来源的交易
     * id 来自 transaction表里的数据
     * 处理逻辑
     * 1. 根据前端传来的id ，获取完整数据
     * 2. 单独存储领用记录
     * 3. 冲减库存
     */
    @Transactional(rollbackFor = Exception.class)
    public void processUseTransaction(String transactionId) {
        log.info("处理Use交易: {}", transactionId);
        
        try {
            Transaction transaction = this.transactionMapper.selectById(transactionId);
            Long stockId = transaction.getStockId();
            BigDecimal qty = transaction.getQty();
            //保存领用记录
            Long userRecordId = saveUsedRecord(transaction);
            // 获取当前库存
            StockStatus stock = this.stockStatusMapper.selectById(stockId);
            if (stock != null) {
                BigDecimal currentQty = new  BigDecimal (stock.getStockQty());
                // 更新库存数量， 领用数据要减法， 下面这个更新方法是 在sql 做了 stock_qty+qty ,所以传负数
                updateStockQuantity(stockId, qty.negate());
                // 更新交易状态
                BigDecimal finalQty = currentQty.subtract(qty);

                Transaction  tmp  = new Transaction();
                tmp.setId(Long.valueOf(transactionId));
                tmp.setCalcStatus(1);
                tmp.setReCalcStatus(1);
                tmp.setUpdateRemarks("领用");
                tmp.setUponCalc(finalQty);
                tmp.setGrId(userRecordId);
                this.transactionMapper.updateById(tmp);

                log.info("Use交易处理完成: transaction id : {}", transactionId);
            }else {
                log.warn("Use交易处理出错，没有找到库存: transaction id : {}", transactionId);
                Transaction  tmp  = new Transaction();
                tmp.setId(Long.valueOf(transactionId));
                tmp.setCalcStatus(0);
                tmp.setReCalcStatus(2);//标识计算库存错误
                tmp.setUpdateRemarks("找不到库存");
                this.transactionMapper.updateById(tmp);
            }
            
        }  catch (Exception e) {
            log.error("处理Use交易失败 领用transaction id :{}",transactionId, e);
            //标记处理失败
            Transaction tmp = new Transaction();
            tmp.setId(Long.valueOf(transactionId));
            tmp.setCalcStatus(0);
            tmp.setReCalcStatus(2);
            this.transactionMapper.updateById(tmp);
        }
    }
    
    /**
     * 处理 Damage 报损
     */
    @Transactional(rollbackFor = Exception.class)
    public void processDamageTransaction(Long transactionId) {
        log.info("处理Damage交易: transactionId {}", transactionId);
        
        try {
            Transaction damageTransaction = this.transactionMapper.selectById(transactionId);
            Long stockId = damageTransaction.getStockId();
            BigDecimal qty = damageTransaction.getQty();
            // 获取当前库存
            StockStatus stock = this.stockStatusMapper.selectById(stockId);
            if (stock != null) {
                BigDecimal currentQty = stock.getStockQty() == null ? BigDecimal.ZERO: new  BigDecimal (stock.getStockQty());
                BigDecimal currentHoldQty = stock.getHoldQty() == null ? BigDecimal.ZERO : new  BigDecimal (stock.getHoldQty());
                BigDecimal finalQty = currentQty.subtract(qty);
                BigDecimal finalHoldQty = currentHoldQty.subtract(qty);
                // 更新库存数量和冻结数量
                updateStockAndHoldQuantity(stockId, finalQty, finalHoldQty);
                // 更新交易状态
                Transaction newTransaction = new Transaction();
                newTransaction.setId(transactionId);
                newTransaction.setUponCalc(finalQty);
                newTransaction.setCalcStatus(1);
                newTransaction.setReCalcStatus(1);
                newTransaction.setUponCalc(finalQty);
                newTransaction.setUpdateRemarks("报损");
                transactionMapper.updateById(newTransaction);
                log.info("Damage交易处理完成: {}", transactionId);
            }else {
                log.error("处理Damage交易失败,没有找到库存 transactionId :{}",transactionId);
                Transaction tmp = new  Transaction ();
                tmp.setId(transactionId);
                tmp.setReCalcStatus(2);
                tmp.setCalcStatus(0);
                tmp.setUpdateRemarks("处理Damage交易失败,没有找到库存");
            }
        }  catch (Exception e) {
            log.error("处理Damage交易失败 transactionId :{}",transactionId, e);
            Transaction tmp = new  Transaction ();
            tmp.setId(transactionId);
            tmp.setReCalcStatus(2);
            tmp.setCalcStatus(0);
            tmp.setUpdateRemarks("处理Damage交易失败,没有找到库存");
        }
    }
    
    /**
     * 处理调拨申请
     * 按 status 字段分别处理 Pending/Approved/Rejected
     *  - Pending：查找有权限的用户，生成审批链接并邮件通知
     *  - Approved/Rejected：通知申请人结果
     */
    @Transactional(rollbackFor = Exception.class)
    public void processTransferRequest(Map<String,Object> params) {
        log.info("开始处理调拨申请: ");
        try {
            String status = params.get("status").toString();
            
            if ("Pending".equals(status)) {
                processPendingTransferRequest(params);
            } else if ("Approved".equals(status) || "Rejected".equals(status)) {
                processApprovedRejectedTransferRequest(params);
            } else {
                log.warn("未知的调拨申请状态: {}", status);
            }
            
        } catch (Exception e) {
            log.error("处理调拨申请文件失败: ",  e);
        }
    }
    
    /**
     * 处理待审批的调拨申请
     */
    private void processPendingTransferRequest(Map<String,Object> transferRequest) {
        log.info("处理待审批调拨申请: {}", transferRequest.get("id").toString());
        
        try {
            // 查找有权限的用户
            List<Map<String, Object>> users = findUsersWithTransferPermission();
            
            for (Map<String, Object> user : users) {
                String email = (String) user.get("login");
                if (isValidEmail(email)) {
                    // 获取调拨物品信息
                    List<Map<String, Object>> transferItems = getTransferItems(Long.valueOf(transferRequest.get("id").toString()), user);
                    
                    if (!transferItems.isEmpty()) {

                        if(isPermission(user,Long.valueOf(transferItems.get(0).get("site_id").toString())) ||isPermission(user,Long.valueOf(transferItems.get(0).get("to_site_id").toString()))  ) {
                            // 发送邮件通知
                            sendTransferRequestEmail(email, transferRequest, transferItems, user);

                            // 如果有备用邮箱，也发送通知
                            String altEmail = (String) user.get("alt_email");
                            if (altEmail != null && isValidEmail(altEmail)) {
                                sendTransferRequestEmail(altEmail, transferRequest, transferItems, user);
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            log.error("处理待审批调拨申请失败 transferRequest id :{}", transferRequest.get("id").toString(), e);
        }
    }
    
    /**
     * 处理已审批/拒绝的调拨申请
     */
    private void processApprovedRejectedTransferRequest(Map<String,Object> transferRequest) {
        log.info("处理已审批/拒绝调拨申请: {}", transferRequest.get("id").toString());
        
        try {
            // 获取申请人信息
            Map<String, Object> requester = getTransferRequestRequester(Long.valueOf(transferRequest.get("id").toString()));
            
            if (requester != null) {
                String email = (String) requester.get("login");
                if (isValidEmail(email)) {
                    // 获取调拨物品信息
                    List<Map<String, Object>> transferItems = getTransferItemsForNotification(Long.valueOf(transferRequest.get("id").toString()));
                    
                    if (!transferItems.isEmpty()) {
                        // 发送结果通知邮件
                        sendTransferResultEmail(email, transferRequest, transferItems);
                    }
                }
            }

        } catch (Exception e) {
            log.error("处理已审批/拒绝调拨申请失败", e);
        }
    }
    
    /**
     * 处理损坏报告文件
     * 传的参数结构如下：
     * id: damage_report表的id,
     * stock_id: 库存id,
     * qty: qty,
     * remarks: remarks??null,
     * reporter_user_id: req.loggedin_user.user_id,
     * status: 'Pending'
     */
    @Transactional(rollbackFor = Exception.class)
    public void processDamageReport(Map<String,Object> params) {
        log.info("开始处理损坏报告文件: ");
        try {
            String status = params.get("status").toString();
            if ("Pending".equals(status)) {
                processPendingDamageReport(params);
            } else {
                log.warn("未知的损坏报告状态: {}", status);
            }
            
        } catch (Exception e) {
            log.error("处理损坏报告文件失败: ", e);
        }
    }
    
    /**
     *
     * 处理待确认的损坏报告
     * 读取并解析，status 为 Pending 时，查找有权限的用户，生成确认链接并邮件通知
     */
    private void processPendingDamageReport(Map<String,Object> params) {
        log.info("处理待确认损坏报告: damage_report_id {}", params.get("id").toString());
        
        try {
            // 查找有权限的用户
            List<Map<String, Object>> users = findUsersWithDamagePermission();
            
            for (Map<String, Object> user : users) {
                String email = (String) user.get("login");
                if (isValidEmail(email)) {
                    // 获取损坏报告信息
                    List<Map<String, Object>> damageInfo = getDamageReportInfo(Long.valueOf(params.get("id").toString()), user);
                    
                    if (!damageInfo.isEmpty()) {
                        if(isPermission(user,Long.valueOf(damageInfo.get(0).get("site_id").toString()))){
                            // 发送邮件通知
                            sendDamageReportEmail(email, params, damageInfo, user);
                            
                            // 如果有备用邮箱，也发送通知
                            String altEmail = (String) user.get("alt_email");
                            if (altEmail != null && isValidEmail(altEmail)) {
                                sendDamageReportEmail(altEmail, params, damageInfo, user);
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            log.error("处理待确认损坏报告失败", e);
        }
    }
    
    // ==================== 数据库操作方法 ====================
    
    /**
     * 处理站点数据
     */
    private Long processSiteData(String projectCode) {
        EntityWrapper<MstrSite> wrapper = new EntityWrapper<>();
        wrapper.eq("project_code", projectCode).eq("is_deleted", 0);
        List<MstrSite> sites = mstrSiteMapper.selectList(wrapper);
        // 如果没有则新增，插入新站点
        if (sites.isEmpty()) {
            MstrSite newSite = new MstrSite();
            newSite.setProjectCode(projectCode);
            newSite.setIsDeleted(0);
            mstrSiteMapper.insert(newSite);
            return  newSite.getId();
        }
        return sites.get(0).getId();
    }
    
    /**
     * 处理计量单位数据
     */
    private Long processUomData(String uomDesc) {
        EntityWrapper<MstrUom> wrapper = new EntityWrapper<>();
        wrapper.eq("desc_en", uomDesc).eq("is_deleted", 0);
        List<MstrUom> uoms = mstrUomMapper.selectList(wrapper);
        
        if (uoms.isEmpty()) {
            // 插入新计量单位
            MstrUom newUom = new MstrUom();
            newUom.setDescEn(uomDesc);
            newUom.setIsDeleted(0);
            mstrUomMapper.insert(newUom);
            return newUom.getId();
        }
        return uoms.get(0).getId();
    }
    
    /**
     * 处理物品数据
     */
    private void processItemData(GrItem item, Long uomId) {
        String itemDesc = item.getItemdesc();
        EntityWrapper<MstrItems> wrapper = new EntityWrapper<>();
        wrapper.eq("item_desc", itemDesc).eq("uom_id", uomId).eq("is_deleted", 0);
        List<MstrItems> items = mstrItemsMapper.selectList(wrapper);
        
        if (items.isEmpty()) {
            // 处理物品分类
            String catId = processItemCategory(item.getProdcat());
            
            // 插入新物品
            MstrItems newItem = new MstrItems();
            newItem.setCatId(catId);
            newItem.setItemBarcode(UUID.randomUUID().toString());
            newItem.setItemDesc(itemDesc);
            newItem.setUomId(uomId);
            newItem.setIsExpansive(0);
            newItem.setCheckExpiry(0);
            newItem.setCreatedt(new Date());
            newItem.setIsDeleted(0);
            mstrItemsMapper.insert(newItem);
            //写到gr item
            item.setItemBarcode(newItem.getItemBarcode());
        }else {
            //写到gr item
            item.setItemBarcode(items.get(0).getItemBarcode());
        }

    }
    
    /**
     * 处理物品分类
     */
    private String processItemCategory(String prodCat) {
        if (prodCat == null || prodCat.isEmpty() || prodCat.length() < 12) {
            return prodCat;
        }
        // 按4位一组分类
        String cat00Code = prodCat.substring(0, 4);
        String cat01Code = prodCat.substring(4, 8);
        String cat02Code = prodCat.substring(8, 12);
        // 处理第一级分类
        Long cat00Id = processCategoryLevel("mstr_cat_00", cat00Code, null);
        // 处理第二级分类
        processCategoryLevel("mstr_cat_01", cat01Code, Long.valueOf(cat00Code));
        // 处理第三级分类
        processCategoryLevel("mstr_cat_02", cat02Code, Long.valueOf(cat01Code));
        return prodCat;
    }
    
    /**
     * 处理分类层级
     */
    private Long processCategoryLevel(String tableName, String code, Long parentId) {
        // 根据表名选择对应的Mapper
        if ("mstr_cat_00".equals(tableName)) {
            EntityWrapper<MstrCat00> wrapper = new EntityWrapper<>();
            wrapper.eq("id", code).eq("is_deleted", 0);
            List<MstrCat00> categories = mstrCat00Mapper.selectList(wrapper);
            if (categories.isEmpty()) {
                mstrCat00Mapper.insertWithCustomId(Long.parseLong(code),code,0);
                return Long.parseLong(code);
            }
            return categories.get(0).getId();
        } else if ("mstr_cat_01".equals(tableName)) {
            EntityWrapper<MstrCat01> wrapper = new EntityWrapper<>();
            wrapper.eq("id", code).eq("is_deleted", 0);
            List<MstrCat01> categories = mstrCat01Mapper.selectList(wrapper);
            if (categories.isEmpty()) {
                mstrCat01Mapper.insertWithCustomId(Long.parseLong(code),code,code,parentId,0);
                return Long.parseLong(code);
            }
            return categories.get(0).getId();
        } else if ("mstr_cat_02".equals(tableName)) {
            EntityWrapper<MstrCat02> wrapper = new EntityWrapper<>();
            wrapper.eq("id", code).eq("is_deleted", 0);
            List<MstrCat02> categories = mstrCat02Mapper.selectList(wrapper);
            if (categories.isEmpty()) {
                mstrCat02Mapper.insertWithCustomId(Long.parseLong(code),code,parentId,0);
                return Long.parseLong(code);
            }
            return categories.get(0).getId();
        }
        return null;
    }
    
    /**
     * 处理库存状态
     */
    private StockStatus processStockStatus(GrItem item, Long siteId, String itemBarcode, Long uomId,Date grRecordTime) {
        // 查询现有库存状态
        StockStatus stockStatus = this.getStockStatus(siteId, uomId, itemBarcode);
        if (stockStatus == null) {            // 创建新的库存状态
            return createNewStockStatus(item, siteId, grRecordTime);
        }else {
            return updateStock(item,stockStatus,grRecordTime);
        }

    }

    private StockStatus updateStock(GrItem item, StockStatus existStock,Date grRecordTime) {
        StockStatus tmp = new StockStatus();
        tmp.setId(existStock.getId());
        BigDecimal stockQty = existStock.getStockQty()==null?BigDecimal.ZERO:new BigDecimal(existStock.getStockQty());
        stockQty = stockQty.add(item.getQty());
        tmp.setStockQty(stockQty.doubleValue());
        tmp.setLastGr(grRecordTime);
        tmp.setLastUpdate(grRecordTime);
        this.stockStatusMapper.updateById(tmp);
        return tmp;
    }
    
    /**
     * 创建新的库存状态
     */
    private StockStatus createNewStockStatus(GrItem item,Long siteId, Date recordTime) {
        StockStatus newStock = new StockStatus();
        newStock.setSiteId(siteId);
        newStock.setItemCatId(item.getProdcat());
        newStock.setItemBarcode(item.getItemBarcode());
        newStock.setItemDesc(item.getItemdesc());
        newStock.setItemUomId(item.getUomId());
        newStock.setStockQty(item.getQty().doubleValue());
        newStock.setHoldQty(0d);
        newStock.setRecordDate(recordTime);
        newStock.setLastGr(recordTime);
        newStock.setLastUpdate(recordTime);

        stockStatusMapper.insert(newStock);

        return newStock;
    }
    
    /**
     * gr 入库写交易记录
     */
    private Long processTransactionRecord(Gr gr, GrItem item, StockStatus stock) {

        Transaction trx = new Transaction();
        trx.setSuppName(gr.getSuppnam());
        trx.setStockId(stock.getId());
        trx.setQty(item.getQty());
        trx.setRemarks("GR入库");
        trx.setUponCalc(new BigDecimal(stock.getStockQty()));
        trx.setNewBalanceQty(new BigDecimal(stock.getStockQty()));
        trx.setUpdateRemarks("GR入库");
        trx.setReCalcStatus(1);
        trx.setIsDeleted(0);
        trx.setCalcStatus(1);
        trx.setSource("GR");
        trx.setRefNo(gr.getRefno());
        trx.setPoNo(gr.getPono());
        trx.setPrNo(gr.getPrno());
        trx.setScForm(item.getScform());
        trx.setDnDirName(gr.getDnDirName());
        trx.setTmmUrl(gr.getUrl());
        trx.setRecvSiteId(gr.getRecvSiteId());
        trx.setGrId(gr.getId());
        trx.setGrItemId(item.getId());
        trx.setIsTransfer(0);
        trx.setRecordTime(gr.getRecordTime());
        transactionMapper.insert(trx);
        return trx.getId();
    }
    

    
    /**
     * 更新收货单状态
     */
    private  void updateGrStatus(Long grId) {
        // 更新收货单状态
        Gr gr = new Gr();
        gr.setId(grId);
        gr.setCalcStatus(1);
        gr.setReCalcStatus(1);
        grMapper.updateById(gr);
        
        // 更新收货单明细状态
        EntityWrapper<GrItem> wrapper = new EntityWrapper<>();
        wrapper.eq("gr_id", grId);
        GrItem tmp = new GrItem();
        tmp.setCalcStatus(1);
        tmp.setReCalcStatus(1);
        grItemMapper.update(tmp,wrapper);
    }
    
    /**
     * 更新库存数量
     */
    private void updateStockQuantity(Long stockId, BigDecimal qty) {
        stockStatusMapper.updateStockQuantity(stockId, qty);
    }

    
    /**
     * 更新库存和冻结数量
     */
    private void updateStockAndHoldQuantity(Long stockId, BigDecimal stockQty, BigDecimal holdQty) {
        stockStatusMapper.updateStockAndHoldQuantity(stockId, stockQty, holdQty);
    }
    
    /**
     * 保存领用记录
     * 将Transaction数据转换为UseRecord并保存
     */
    private Long saveUsedRecord(Transaction transaction) {
            UseRecord useRecord = new UseRecord();
            // 复制基本字段
            useRecord.setStockId(transaction.getStockId());
            useRecord.setSuppName(transaction.getSuppName());
            useRecord.setRefNo(transaction.getRefNo());
            useRecord.setPoNo(transaction.getPoNo());
            useRecord.setPrNo(transaction.getPrNo());
            useRecord.setScForm(transaction.getScForm());
            useRecord.setUserId(transaction.getUserId());
            useRecord.setAltEmail(transaction.getAltEmail());
            useRecord.setQty(transaction.getQty());
            useRecord.setPickedBy(transaction.getPickedBy());
            useRecord.setRemarks(transaction.getRemarks());
            useRecord.setCalcStatus(transaction.getCalcStatus());
            useRecord.setDnDirName(transaction.getDnDirName());
            useRecord.setTmmUrl(transaction.getTmmUrl());
            useRecord.setIsTransfer(transaction.getIsTransfer());
            useRecord.setLatitude(transaction.getLatitude());
            useRecord.setLongitude(transaction.getLongitude());
            useRecord.setSource(transaction.getSource());
            
            // 设置领用时间
            if (transaction.getRecordTime() != null) {
                useRecord.setUsedTime(transaction.getRecordTime());
            }
            
            // 设置记录时间
            useRecord.setRecordTime(new Date());
            
            // 设置默认值
            useRecord.setIsDeleted(0);
            
            // 保存领用记录
            useRecordService.save(useRecord);
            return useRecord.getId();


    }
    
    // ==================== 邮件相关方法 ====================
    
    /**
     * 查找有调拨权限的用户
     */
    private List<Map<String, Object>> findUsersWithTransferPermission() {
        return userMapper.selectUsersWithTransferPermission();
    }
    
    /**
     * 查找有损坏报告权限的用户
     */
    private List<Map<String, Object>> findUsersWithDamagePermission() {
        return userMapper.selectUsersWithDamagePermission();
    }
    
    /**
     * 获取调拨物品信息
     */
    private List<Map<String, Object>> getTransferItems(Long requestId, Map<String, Object> user) {
        return transferItemsMapper.selectTransferItems(requestId);
    }
    
    /**
     * 获取调拨申请申请人
     */
    private Map<String, Object> getTransferRequestRequester(Long requestId) {
        return userMapper.selectTransferRequestRequester(requestId);
    }
    
    /**
     * 获取调拨物品信息（用于通知）
     */
    private List<Map<String, Object>> getTransferItemsForNotification(Long requestId) {
        return transferItemsMapper.selectTransferItemsForNotification(requestId);
    }
    
    /**
     * 获取损坏报告信息
     */
    private List<Map<String, Object>> getDamageReportInfo(Long reportId, Map<String, Object> user) {
        return damageReportMapper.selectDamageReportInfo(reportId);
    }
    
    /**
     * 验证邮箱格式
     */
    private boolean isValidEmail(String email) {
        if (email == null || email.trim().isEmpty()) {
            return false;
        }
        String emailRegex = "^[\\s]*\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*[\\s]*$";
        return email.matches(emailRegex);
    }
    
    /**
     * 发送调拨申请邮件
     */
    private void sendTransferRequestEmail(String email, Map<String, Object> transferRequest, List<Map<String, Object>> transferItems, Map<String, Object> user) {
        try {
            String subject = "[IMS] Relocation Request Alert";
            StringBuilder body = new StringBuilder();
            
            // 获取目标站点信息
            String toSiteInfo = getToSiteInfo(Long.valueOf(transferRequest.get("to_site_id").toString()));
            body.append("Following item(s) will be relocated to ").append(toSiteInfo).append(":<br />");
            
            for (Map<String, Object> item : transferItems) {
                String fromSite = (String) item.get("from_project_code") + 
                                ((String) item.get("from_site_info") != null ? ("-" + item.get("from_site_info")) : "");
                body.append(item.get("item_desc"))
                    .append(" ((From) Project/Site: ").append(fromSite)
                    .append(", Current Stock Qty: ").append(item.get("current_stock_qty"))
                    .append(" ").append(item.get("uom"))
                    .append(", Relocation Qty: ").append(item.get("reloc_qty"))
                    .append(" ").append(item.get("uom"))
                    .append(")<br />");
            }
            
            // 如果有审批权限，添加审批链接
            if (1== Integer.valueOf(user.get("ops_stock_item_reloc_apprv").toString())) {
                String token = generateToken();
                String link = createApprovalLink(Long.valueOf(transferRequest.get("id").toString()), user.get("id"), token);
                body.append("<br />Please click <a href=\"").append(link).append("\">here</a> to approve");
            }
            
            emailService.sendEmail(email, subject, body.toString());
            
        } catch (Exception e) {
            log.error("发送调拨申请邮件失败", e);
        }
    }
    
    /**
     * 发送调拨结果邮件
     */
    private void sendTransferResultEmail(String email, Map<String, Object> transferRequest, List<Map<String, Object>> transferItems) {
        try {
            String status = transferRequest.get("status").toString();
            String subject = "[IMS] Relocation Request " + status;
            StringBuilder body = new StringBuilder();
            
            body.append("Following relocation request(s) have/has been ")
                .append("Approved".equals(status) ? "approved" : "rejected")
                .append(":<br />");
            
            for (Map<String, Object> item : transferItems) {
                String fromSite = (String) item.get("from_project_code") + 
                                ((String) item.get("from_site_info") != null ? ("-" + item.get("from_site_info")) : "");
                String toSite = (String) item.get("to_project_code") + 
                              ((String) item.get("to_site_info") != null ? ("-" + item.get("to_site_info")) : "");
                
                body.append(item.get("item_desc"))
                    .append(" ((From) Project/Site: ").append(fromSite)
                    .append(", (To) Project/Site: ").append(toSite)
                    .append(", Current Stock Qty: ").append(item.get("current_stock_qty"))
                    .append(" ").append(item.get("uom"))
                    .append(", Relocation Qty: ").append(item.get("reloc_qty"))
                    .append(" ").append(item.get("uom"))
                    .append(")<br />");
            }
            
            emailService.sendEmail(email, subject, body.toString());
            
        } catch (Exception e) {
            log.error("发送调拨结果邮件失败", e);
        }
    }
    
    /**
     * 发送损坏报告邮件
     * 参考原 Node.js 代码实现，包含用户权限判断和令牌链接数据维护
     */
    private void sendDamageReportEmail(String email, Map<String, Object> damageReport, List<Map<String, Object>> damageInfo, Map<String, Object> user) {

            String subject = "[IMS] Damage Report Alert";
            StringBuilder body = new StringBuilder();
            
            body.append("Following damaged item(s):<br />");
            
            Map<String, Object> item = damageInfo.get(0);
            String siteInfo = (String) item.get("project_code") + 
                            ((String) item.get("site_info") != null ? ("-" + item.get("site_info")) : "");
            
            body.append(item.get("item_desc"))
                .append(" (Project/Site: ").append(siteInfo)
                .append(", Damage Qty: ").append(item.get("damage_qty"))
                .append(" ").append(item.get("uom"))
                .append(")<br />");
            
            // 参考原 Node.js 代码：检查用户权限并生成令牌链接
            boolean recView = 1 == (Integer)user.get("ops_stock_item_dmg_rec_viewing");
            boolean recack = 1 == (Integer)user.get("ops_stock_item_dmg_rec_ack");
            
            if (recView && recack) {
                // 生成随机令牌（替代 crypto.randomBytes(64).toString('hex')）
                String token = com.freeter.modules.gjs.util.PasswordUtil.generateRandomToken();
                
                // 创建令牌链接记录（替代 damage_report_token_link 表的插入）
                Long insertedId = createDamageReportTokenLink(
                    Long.valueOf(damageReport.get("id").toString()),
                    (Long) user.get("id"),
                    token,
                    email
                );
                
                if (insertedId != null) {
                    // 生成确认链接（使用插入后的ID）
                    String link = createDamageAckLink(insertedId, token);
                    body.append("<br />Please click <a href=\"").append(link).append("\">here</a> to approve");
                }
            }
            emailService.sendEmail(email, subject, body.toString());

    }
    
    /**
     * 获取目标站点信息
     */
    private String getToSiteInfo(Long siteId) {
        MstrSite site = mstrSiteMapper.selectById(siteId);
        if (site != null && site.getIsDeleted() == 0) {
            return site.getProjectCode() + 
                   (site.getSiteInfo() != null ? ("-" + site.getSiteInfo()) : "");
        }
        return "";
    }
    
    /**
     * 生成令牌
     */
    private String generateToken() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    
    /**
     * 创建审批链接
     */
    private String createApprovalLink(Long requestId, Object userId, String token) {
        // 这里应该保存令牌到数据库，简化处理
        return "http://localhost:8080/link/approve-reloc-app?id=" + requestId + "&token=" + token;
    }
    
    /**
     * 创建损坏确认链接
     */
    private String createDamageAckLink(Long tokenLinkId, String token) {
        // 使用令牌链接ID和令牌生成确认链接
        return "http://localhost:8080/link/acknowledge-damage?id=" + tokenLinkId + "&token=" + token;
    }
    
    /**
     * 创建损坏报告令牌链接记录
     * 替代原 Node.js 代码中的 damage_report_token_link 表插入操作
     */
    private Long createDamageReportTokenLink(Long reportId, Long userId, String token, String email) {
            // 创建令牌链接实体
            com.freeter.modules.gjs.entity.DamageReportTokenLink tokenLink = 
                new com.freeter.modules.gjs.entity.DamageReportTokenLink();
            
            tokenLink.setReportId(reportId);
            tokenLink.setUserId(userId);
            // 存储令牌的哈希值（替代 bcrypt.hashSync(token, 5)）
            tokenLink.setToken(com.freeter.modules.gjs.util.PasswordUtil.hashToken(token));
            tokenLink.setEmail(email);
            tokenLink.setLastUpdateTime(new Date());
            tokenLink.setIsUsed(0);
            tokenLink.setCreatedAt(new Date());
            tokenLink.setIsDeleted(0);
            
            // 插入数据库
            damageReportTokenLinkMapper.insert(tokenLink);
            
            log.info("损坏报告令牌链接创建成功: reportId={}, userId={}, tokenLinkId={}", 
                reportId, userId, tokenLink.getId());
            
            return tokenLink.getId();
    }

    /**
     * 检查用户是否有权限访问损坏报告相关的库存站点
     * 基于Node.js源码的逻辑实现：
     * 1. 如果用户有user_project_association权限，则直接返回true
     * 2. 否则检查用户是否通过部门关联或直接站点关联有权限访问该库存的站点
     * 
     * @param user 用户信息
     * @param siteId siteId 从 damage_report的stockid 关联库存获取到
     * @return 是否有权限
     */
    private boolean isPermission(Map<String, Object> user, Long siteId) {
        try {
            // 如果用户有user_project_association权限，直接返回true
            Object userProjectAssociation = user.get("user_project_association");
            if (userProjectAssociation != null && 1 == (Integer) userProjectAssociation) {
                return true;
            }

            
            Long userId = (Long) user.get("id");
            if (userId == null) {
                log.warn("用户信息中缺少用户ID");
                return false;
            }
            
            // 检查用户是否通过部门关联有权限访问该站点
            boolean hasDivisionPermission = checkUserDivisionPermission(userId, siteId);
            if (hasDivisionPermission) {
                return true;
            }
            
            // 检查用户是否直接关联该站点
            boolean hasDirectSitePermission = checkUserDirectSitePermission(userId, siteId);
            if (hasDirectSitePermission) {
                return true;
            }
            
            log.info("用户 {} 没有权限访问损坏报告 {} 相关的站点", userId, siteId);
            return false;
            
        } catch (Exception e) {
            log.error("检查用户权限时发生错误，userId: {}, damageReportId: {}", user.get("id"), siteId, e);
            return false;
        }
    }
    
    /**
     * 检查用户是否通过部门关联有权限访问该站点
     */
    private boolean checkUserDivisionPermission(Long userId, Long siteId) {
        try {
            // 查询用户关联的部门
            List<Map<String, Object>> userDivisions = userMapper.selectUserDivisionsByUserId(userId,siteId);
            if (userDivisions != null  && !userDivisions.isEmpty()) {
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("检查用户部门权限时发生错误，userId: {},  siteInfo: {}", userId, siteId, e);
            return false;
        }
    }
    
    /**
     * 检查用户是否直接关联该站点
     */
    private boolean checkUserDirectSitePermission(Long userId,  Long siteId) {
        try {
            // 查询用户直接关联的站点
            EntityWrapper<UsersSites> wrapper = new EntityWrapper<>();
            wrapper.eq("user_id", userId).eq("site_id", siteId);
            List<UsersSites> userSites = usersSitesMapper.selectList(wrapper);
            if (userSites != null && !userSites.isEmpty()) {
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("检查用户直接站点权限时发生错误，userId: {}, projectCode: {}, site Id: {}", userId, siteId, e);
            return false;
        }
    }
    
    /**
     * 检查部门站点是否匹配项目代码和站点信息
     */
    private boolean isSiteMatch(DivisionsSitesDetailDTO divisionSite, String projectCode, String siteInfo) {
        try {
            // 使用DivisionsSitesDetailDTO中的siteCode字段
            String siteCode = divisionSite.getSiteCode();
            if (siteCode != null) {
                // 检查站点代码是否匹配项目代码和站点信息
                return isSiteCodeMatch(siteCode, projectCode, siteInfo);
            }
            return false;
        } catch (Exception e) {
            log.error("检查站点匹配时发生错误", e);
            return false;
        }
    }
    
    /**
     * 检查站点代码是否匹配项目代码和站点信息
     */
    private boolean isSiteCodeMatch(String siteCode, String projectCode, String siteInfo) {
        try {
            // 这里需要根据实际的数据库结构来实现
            // 假设siteCode包含项目代码和站点信息，或者需要查询数据库来匹配
            // 简化实现：直接比较siteCode是否包含projectCode
            return siteCode != null && siteCode.contains(projectCode);
        } catch (Exception e) {
            log.error("检查站点代码匹配时发生错误", e);
            return false;
        }
    }
} 